package disjunction.extensions.view
{
	import flash.display.BitmapData;
	import flash.geom.Point;
	import flash.geom.Rectangle;

	/**
	 * Stores frame state (base frame + effects applied); enables blitting ops.
	 * When drawing scaled, rotated or with an effect, it will check for cached
	 * versions; if none exist, it will produce and cache them on the fly (?).
	 * 
	 * The frame is fairly relevant in the model, as certain frames may be reqd
	 * for logic -- eg. mid-swing of an attack. Should these be in model? Or do
	 * we just store a "ratio complete" within the current animation (also
	 * stored)?
	 * 
	 * Anything unsure *should* be stored here -- for cases where it is *not*
	 * in the model. Same as scale, rotation?
	 * 
	 * (not implemented, could be a straight copy of the entire spritesheet) overlay/flash applied
	 */
		
	public class BitmapSprite
	{
		//this is a drawing position. it doesn't reflect the model's position, directly.
		//this is not like flash's Sprite.
		//There is no need to actually store state here; although some implementations
		//(non-MVC ones) might use such.
		public var x:Number;
		public var y:Number;
		
		//rotations are applied *before* scaling. (There could be a toggle for this)
		//TODO implement
		public var scale:int = 1;
		public var scaleX:Number;
		public var scaleY:Number;
		
		/** Sets the rotation, in radians. */
		public var rotation:Number;//radians
		//private var _rotationRad:Number = 0;
		
		private var _spriteSheet:BitmapDataSpriteSheet;
		private var _animation:String;
		private var _frame:uint = 0;
		
		
		//TODO implement
		public var isLooping:Boolean = true;
		
		//TODO implement
		public var antialiased:Boolean = false;
		
		private var _aabb:Rectangle = null; //extend to an AABB class which calcs centre/origin.
		
		public function BitmapSprite(spriteSheet:BitmapDataSpriteSheet, defaultAnimation:String)//, registrationMode:String )
		{
			_spriteSheet = spriteSheet;
			_animation = defaultAnimation;
		}

		/**
		 * Blits the current frame to the destination BitmapData.
		 * @param destination The BitmapData instance to draw the sprite frame to.
		 * @param position The position at which to draw the sprite frame.
		 */
		public function draw(destination:BitmapData):void
		{
			//Ideally this would be:
			//_frameRect.x = _frameRect.y = 0;
			//destination.copyPixels(_spriteSheet.getFrame(animation, frame, rotation), _frameRect, position);
			//...but getFrame() would necessitate the creation of additional BitmapData objects which is
			//likely slow due to memory allocation. 
			_spriteSheet.drawFrame(_animation, _frame, rotationIndex, destination, position);
		
		}
		
		
		/**
		 * Pick a random base frame of animation using the given extended parameters
		 * (scale, rotation, overlay etc.)
		 */
		public function goToRandomAnimation():void
		{
			_frame = int(Math.random() * numFramesInAnimation);
			//TODO implement
		}
		
		/**
		 * Pick a random base frame of animation using the given extended parameters
		 * (scale, rotation, overlay etc.)
		 */
		public function goToRandomFrame():void
		{
			_frame = int(Math.random() * numFramesInAnimation);
			//TODO implement
		}
		
		//use a single, global animation timer in the View which calls
		//nextFrame as appropriate on every sprite
		public function goToNextFrame():void
		{
			//TODO
			_frame++;
			if (_frame > numFramesInAnimation)
			{
				if (isLooping)
					_frame = 0; //return to start frame.
				else
					_frame--; //stay on last frame.
			}
		}
		
		public function set animation(value:String):void
		{
			_animation = value;
		}	
		
		/** Converts an angle of rotation in radians to the appropriate index used in the spritesheet. */
		private function get rotationIndex():uint
		{	
			if (_spriteSheet.numRotations == 1)
				return 0;
			
			//TODO clean up, there's probably way more going on here than there needs to be.
			var segmentRatio:Number = 1 / _spriteSheet.numRotations;
			//var segmentRad:Number = Math.PI / _spriteSheet.numRotations; //num radians per segment
			var ratio:Number = rotation / Math.PI; //at what ratio 0->1 along circle's circumference?
			ratio *= _spriteSheet.numRotations; //bring into range of numRotations
			ratio += segmentRatio * .5; //offset by half a segment for each segment arc
			//trace(ratio);
			return uint(ratio);
		}
		
		public function get numFramesInAnimation():uint
		{
			return _spriteSheet.numFramesInAnimation(_animation);
		}
		
		public function get aabb():Rectangle
		{
			/*
			var rotationMatrix:Matrix = new Matrix();
			rotationMatrix.translate(-32, -32);
			rotationMatrix.rotate(radians);
			rotationMatrix.translate(32, 32);
			*/
			//TODO
			return null;
		}
	}
}